﻿using System;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows.Controls;

namespace System.Data
{
    /// <summary>
    /// Provides DataColumn functionality
    /// </summary>
    public class DataColumn
    {
        #region Static Variables
        /// <summary>
        /// Defines "msdata" prefix
        /// </summary>
        private static XNamespace msdata = "urn:schemas-microsoft-com:xml-msdata";

        /// <summary>
        /// Defines "xs" prefix
        /// </summary>
        private static XNamespace xs = "http://www.w3.org/2001/XMLSchema";
        #endregion

        #region Data Fields
        /// <summary>
        /// "DataGridTextColumn" property data field
        /// </summary>
        private DataGridTextColumn _dataGridTextColumn;

        /// <summary>
        /// "Index" property data field
        /// </summary>
        private int _index;

        /// <summary>
        /// "NullValue" property data field
        /// </summary>
        private string _nullValue;

        /// <summary>
        /// "ReadOnly" property data field
        /// </summary>
//        private bool _readOnly;

        /// <summary>
        /// "Schema" property data field
        /// </summary>
        private XElement _schema;
        #endregion

        #region Events
        /// <summary>
        /// Is reaised when schema was changed
        /// </summary>
        internal event EventHandler<DataSetEventArgs> SchemaChanged;
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public DataColumn()
        {
            //InitializeDataGridTextColumn();
            this.CreateSchema();
            this.ChangeType(Type.GetType("System.String"));
        }

        /// <summary>
        /// Makes a new DataColumn instance assigning name to it
        /// </summary>
        /// <param name="columnName">DataColumn name</param>
        public DataColumn(string columnName)
            : this()
        {
            // Validate argument
            if (string.IsNullOrEmpty(columnName))
            {
                throw new ArgumentNullException("columnName");
            }

            this.RenameSchema(columnName);
        }

        /// <summary>
        /// Makes a new DataColumn instance assigning name and type to it
        /// </summary>
        /// <param name="columnName">DataColumn name</param>
        /// <param name="dataType">DataColumn type</param>
        public DataColumn(string columnName, Type dataType)
            : this()
        {
            // Validate argument
            if (string.IsNullOrEmpty(columnName))
            {
                throw new ArgumentNullException("columnName");
            }

            if (dataType == null)
            {
                throw new ArgumentNullException("dataType");
            }

            this.RenameSchema(columnName);
            this.ChangeType(dataType);
        }

        /// <summary>
        /// Makes DataColumn instance from xml data
        /// </summary>
        /// <param name="schema">Column schema</param>
        internal DataColumn(XElement schema)
        {
            // Validate argument
            if (schema == null)
            {
                throw new ArgumentNullException("schema");
            }

            this._schema = schema;
            SetColumnDatatype();
            //InitializeDataGridTextColumn();
        }

        private void SetColumnDatatype()
        {
            
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets AllowDBNull flag
        /// </summary>
        public bool AllowDBNull
        {
            get
            {
                XAttribute xAttribute = this._schema.Attribute("minOccurs");
                if (xAttribute == null)
                {
                    return false;
                }
                else
                {
                    return this._schema.Attribute("minOccurs").Value == "0";
                }
            }
            set
            {
                if (value == true)
                {
                    this._schema.Attribute("minOccurs").Value = "0";
                }
                else
                {
                    this._schema.Attribute("minOccurs").Value = "1";
                }
            }
        }

        /// <summary>
        /// Gets or sets column caption
        /// </summary>
        public string Caption
        {
            get
            {
                XAttribute xAttribute = this._schema.Attribute(msdata + "Caption");
                if (xAttribute == null)
                {
                    return null;
                }

                return xAttribute.Value;
            }
            set
            {
                // Do nothing if caption was not specified
                if (string.IsNullOrEmpty(value))
                {
                    return;
                }

                XAttribute xAttribute = this._schema.Attribute(msdata + "Caption");
                if (xAttribute == null)
                {
                    this._schema.Add(new XAttribute(msdata + "Caption", value));
                }
                else
                {
                    this._schema.Attribute(msdata + "Caption").Value = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets DataColumn name
        /// </summary>
        public string ColumnName
        {
            get
            {
                return this._schema.Attribute("name").Value;
            }
            set
            {
                // Validate argument
                if (string.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value");
                }

                this.RenameSchema(value);
            }
        }

        /// <summary>
        /// Gets or sets DataGridTextColumn instance
        /// </summary>
        public DataGridTextColumn DataGridTextColumn
        {
            get
            {
                return this._dataGridTextColumn;
            }
            set
            {
                // Validate argument
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }

                this._dataGridTextColumn = value;
            }
        }

        /// <summary>
        /// Gets or sets data type
        /// </summary>
        public Type DataType
        {
            get
            {
                string dataType = this._schema.Attribute("type").Value;
                switch (dataType.Substring(3))
                {
                    case "bool":
                        return Type.GetType("System.Boolean");
                    case "byte":
                        return Type.GetType("System.Byte");
                    case "char":
                        return Type.GetType("System.Char");
                    case "double":
                        return Type.GetType("System.Double");
                    case "decimal":
                        return Type.GetType("System.Decimal");
                    case "float":
                        return Type.GetType("System.Single");
                    case "sbyte":
                        return Type.GetType("System.SByte");
                    case "ushort":
                        return Type.GetType("System.UInt16");
                    case "uint":
                        return Type.GetType("System.UInt32");
                    case "ulong":
                        return Type.GetType("System.UInt64");
                    case "base64Binary":
                        return Type.GetType("System.Byte[]");
                    case "int":
                        return Type.GetType("System.Int32");
                    case "long":
                        return Type.GetType("System.Int64");
                    case "short":
                        return Type.GetType("System.Int16");
                    case "string":
                        return Type.GetType("System.String");
                    case "unsignedByte":
                        return typeof(byte);
                    default:
                        string typeName = "System." + dataType.Substring(3, 1).ToUpper(CultureInfo.InvariantCulture) + dataType.Substring(4);
                        Type type = Type.GetType(typeName);
                        if (type == null)
                        {
                            throw new InvalidOperationException("Cannot find type for type name: " + typeName);
                        }

                        return type;
                }
            }
            set
            {
                this.ChangeType(value);
            }
        }

        /// <summary>
        /// Gets or set default value
        /// </summary>
        public string DefaultValue
        {
            get
            {
                XAttribute xAttribute = this._schema.Attribute("default");
                if (xAttribute == null)
                {
                    return null;
                }
                else
                {
                    if (string.IsNullOrEmpty(this._schema.Attribute("default").Value))
                    {
                        return null;
                    }

                    return this._schema.Attribute("default").Value;
                }
            }
            set
            {
                if (DefaultValue == null)
                {
                    if (string.IsNullOrEmpty(value))
                    {
                        // Was null, came null - do nothing
                    }
                    else
                    {
                        // Was null, came value - add "default" attribute with specified value
                        XAttribute xAttribute = new XAttribute("default", value);
                        this._schema.Add(xAttribute);
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(value))
                    {
                        // Was default value, came null - remove default value
                        XAttribute xAttribute = this._schema.Attribute("default");
                        xAttribute.Remove();
                    }
                    else
                    {
                        // Was default value, came new default value - replace default value by new one
                        this._schema.Attribute("default").Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets column index
        /// </summary>
        internal int Index
        {
            get
            {
                return this._index;
            }
            set
            {
                this._index = value;
            }
        }

        /// <summary>
        /// Gets or sets value which represents null
        /// </summary>
        public string NullValue
        {
            get
            {
                return this._nullValue;
            }
            set
            {
                this._nullValue = value;

                // Revalidate fields in all DataRows
                this.OnSchemaChanged("NullValue", this._nullValue);
            }
        }
/*
        /// <summary>
        /// Gets or sets read only flag
        /// </summary>
        public bool ReadOnly
        {
            get
            {
                return this._readOnly;
            }
            set
            {
                this._readOnly = value;
            }
        }
*/
        /// <summary>
        /// Gets DataColumn schema
        /// </summary>
        internal XElement Schema
        {
            get
            {
                return this._schema;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Changes data type
        /// </summary>
        /// <param name="dataType">Data type</param>
        private void ChangeType(Type dataType)
        {
            // Change type of data in all rows
            this.OnSchemaChanged("ChangeType", dataType.FullName);

            // Change schema
            switch (dataType.FullName)
            {
                case "System.Boolean":
                    this._schema.Attribute("type").Value = "xs:boolean";
                    break;
                case "System.Byte":
                    this._schema.Attribute("type").Value = "xs:unsignedByte";
                    break;
                case "System.Byte[]":
                    this._schema.Attribute("type").Value = "xs:base64Binary";
                    break;
                case "System.DateTime":
                    this._schema.Attribute("type").Value = "xs:dateTime";
                    break;
                case "System.Decimal":
                    this._schema.Attribute("type").Value = "xs:decimal";
                    break;
                case "System.Double":
                    this._schema.Attribute("type").Value = "xs:double";
                    break;
                case "System.Int16":
                    this._schema.Attribute("type").Value = "xs:short";
                    break;
                case "System.Int32":
                    this._schema.Attribute("type").Value = "xs:int";
                    break;
                case "System.Int64":
                    this._schema.Attribute("type").Value = "xs:long";
                    break;
                case "System.String":
                    this._schema.Attribute("type").Value = "xs:string";
                    break;
                //case "VAdvantage.Classes.IDColumn":
                //    this._schema.Attribute("type").Value = "xs:IDColumn";
                //    break;
                //case "VAdvantage.Model.KeyNamePair":
                //    this._schema.Attribute("type").Value = "xs:KeyNamePair";
                //    break;
                default:
                    this._schema.Attribute("type").Value = "xs:object";
                    break;
            }
        }

        /// <summary>
        /// Copies DataColumn structure
        /// </summary>
        /// <returns>Copied DataColumn</returns>
        internal DataColumn Clone()
        {
            DataColumn dataColumn = new DataColumn();
            dataColumn.AllowDBNull = this.AllowDBNull;
            dataColumn.ColumnName = this.ColumnName;
            dataColumn.DataType = this.DataType;
            dataColumn.DefaultValue = this.DefaultValue;
            dataColumn.NullValue = this.NullValue;
            return dataColumn;
        }

        /// <summary>
        /// Creates schema
        /// </summary>
        private void CreateSchema()
        {
            // Load schema
            this._schema = new XElement(xs + "element");
            this._schema.Add(new XAttribute("name", string.Empty));
            this._schema.Add(new XAttribute("type", string.Empty));
            this._schema.Add(new XAttribute("minOccurs", "0"));
        }

        /// <summary>
        /// Initializes DataGridTextColumn instance
        /// </summary>
        private void InitializeDataGridTextColumn()
        {
            //this._dataGridTextColumn = new DataGridTextColumn();
            //this._dataGridTextColumn.Binding = new System.Windows.Data.Binding();
            //this._dataGridTextColumn.FontSize = 11;
        }

        /// <summary>
        /// Raises SchemaChanged event
        /// </summary>
        /// <param name="command">Command for execution</param>
        /// <param name="proposedValue">Proposed value</param>
        private void OnSchemaChanged(string command, string proposedValue)
        {
            if (SchemaChanged != null)
            {
                DataSetEventArgs dataSetEventArgs = new DataSetEventArgs();
                dataSetEventArgs.Command = command;
                dataSetEventArgs.Column = this;
                dataSetEventArgs.ProposedValue = proposedValue;
                SchemaChanged(this, dataSetEventArgs);
            }
        }

        /// <summary>
        /// Renames schema
        /// </summary>
        /// <param name="dataColumnName">DataColumn name</param>
        private void RenameSchema(string dataColumnName)
        {
            this._schema.Attribute("name").Value = dataColumnName;
        }
        #endregion
    }
}
